package cloud.pangui.core.mongo.operate;

import com.mongodb.MongoException;
import cloud.pangui.core.mongo.element.*;
import org.bson.Document;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 查询构建器
 * select * from user where sex = '男' and ( age > 18 or age <10)
 * group by username order by age limit 10,10;
 * <p>
 * [
 * { $match : { score : { $gt : 70, $lte : 90 } } },   相当于where
 * { $group: { _id: null, count: { $sum: 1 } } },
 * <p>
 * ]
 *
 * @Auther hepangui
 * @Date 2020/10/06 0001
*/
public class Query {

	/**
	 * 不允许使用new，请使用from方法
	 */
	protected Query() {
	}
	/**
	 * logger
	 */
//	static Logger logger = Logger.getLogger(Query.class);/**/

	/**
	 * 查询的是哪个表
	 */
	protected String collectionName;

	/**
	 * 内部嵌套查询
	 */
	protected Query innerQuery;

	/**
	 * 查询的字段是什么
	 */
	protected Ele[] selects;

	/**
	 * 查询条件构建器
	 */
	protected Where whereConditions;

	/**
	 * 分组条件
	 */
	protected Ele[] groups;

	/**
	 * 排序条件
	 */
	protected Document orderDoc;

	/**
	 * 分页条件
	 */
	protected int[] limit;



	/**
	 * 获取一个sql形式的quey
	 *
	 * @param sql
	 * @return
	 */
	public static SqlQuery fromSql(String sql) {
		return new SqlQuery(sql);
	}

	/**
	 * 根据表名获取查询
	 *
	 * @param name
	 * @return
	 */
	public static Query from(String name) {
		Query query = new Query();
		query.collectionName = name;
		return query;
	}

	/**
	 * 根据子查询获取查询
	 *
	 * @param query
	 * @return
	 */
	public static Query from(Query query) {
		Query query1 = new Query();
		query1.innerQuery = query;
		return query1;
	}

	/**
	 * 支持一下几种select：
	 * 字符串，无需任何函数，直接返回的
	 * 聚合函数，如max，min等
	 * 其他函数，如substr等
	 * 判断函数
	 * ctrl语句  if else
	 *
	 * @param selects
	 * @return
	 */
	public Query select(Ele... selects) {
		this.selects = selects;
		return this;
	}

	/**
	 * 如果只是简单的获取列属性值，使用此重载的方法即可
	 *
	 * @param selects
	 * @return
	 */
	public Query select(String... selects) {
		Ele[] eles = new Ele[selects.length];
		for (int i = 0; i < selects.length; i++) {
			eles[i] = Field.of(selects[i]);
		}
		this.selects = eles;
		return this;
	}

	/**
	 * 简单where条件，直接传入cond即可
	 * 免去自行构建where条件了
	 *
	 * @param conds
	 * @return
	 */
	public Query where(Cond... conds) {
		int mode = Where.SIMPLE_MODE;
		for (Cond cond : conds) {
			if (!cond.isSimple()) {
				mode = Where.COMPLEX_MODE;
			}
		}
		this.whereConditions = new Where(mode);
		if (conds != null) {
			this.whereConditions.and(conds);
		}
		return this;
	}

	/**
	 * 自定义where condition
	 *
	 * @param conditionBuilder
	 * @return
	 */
	public Query where(Where conditionBuilder) {
		this.whereConditions = conditionBuilder;
		return this;
	}

	/**
	 * 增加一些groupBy的条件
	 *
	 * @param groups
	 * @return
	 */
	public Query groupBy(String... groups) {
		this.groups = Field.of(groups);
		return this;
	}

	/**
	 * 分组元素，支持函数
	 *
	 * @param eles
	 * @return
	 */
	public Query groupBy(Ele... eles) {
		this.groups = eles;
		return this;
	}

	/**
	 * 增加一些orderby的条件
	 *
	 * @param orders
	 * @return
	 */
	public Query orderBy(Order... orders) {
		if (this.orderDoc == null) {
			this.orderDoc = new Document();
		}
		for (Order order : orders) {
			this.orderDoc.append(order.getKey(), order.getType());
		}

		return this;
	}

	/**
	 * 设置查询数量
	 *
	 * @param size
	 * @return
	 */
	public Query limit(int size) {
		return limit(0, size);
	}

	/**
	 * 设置查询其实下标和数量
	 *
	 * @param start 起始下标
	 * @param size  数量
	 * @return
	 */
	public Query limit(int start, int size) {
		this.limit = new int[2];
		this.limit[0] = start;
		this.limit[1] = size;
		return this;
	}

	public List<Document> toQueryDocument(){
		return this.aggregate(false);
	}

	public List<Document> toCountDocument(){
		return this.aggregate(true);
	}

	/**
	 * 管道查询
	 * 对于简单的查询，先where，再group，再order，在limit
	 *
	 * @return
	 */
	protected List<Document> aggregate(boolean count) {
		List<Document> list = new LinkedList<>();
		if (this.innerQuery != null) {
			list.addAll(this.innerQuery.aggregate(count));
		}
		if (this.whereConditions != null && this.whereConditions.build() != null) {
			list.add(new Document("$match", this.whereConditions.build()));
		}
		if(this.selects !=null && this.groups == null){
			for (Ele select : selects) {
				if(select instanceof Aggr){
					this.groupBy(Value.of("1"));
					break;
				}
			}
		}
		if (this.groups != null) {
			list.addAll(buildGroupDoc());
		}
		if (count) {
			list.add(new Document("$count", "count"));
		}else{
			if (this.orderDoc != null ) {
				list.add(new Document("$sort", this.orderDoc));
			}

			// 如果group不为null，那么selelt中的条件已经在group中做过了
			if (this.groups == null && this.selects != null) {
				list.add(buildSelectDoc());
			}
			if (this.limit != null ) {
				if (this.limit[0] > 0) {
					list.add(new Document("$skip", this.limit[0]));
				}
				if (this.limit[1] > 0) {
					list.add(new Document("$limit", this.limit[1]));
				}
			}
		}
		return list;
	}

	/**
	 * 构建select表达式
	 *
	 * @return
	 */
	private Document buildSelectDoc() {
		Document doc = new Document();
		boolean needId = false;
		for (Ele select : this.selects) {
			if (select instanceof Field && "_id".equals(((Field) select).getKey())) {
				needId = true;
			}
			doc.putAll(select.build());
		}
		if (!needId) {
			doc.append("_id", 0);
		}

		return new Document("$project", doc);
	}


	/**
	 * 创建group的管道
	 * 依赖于select
	 * select name ,sex ,max(age) as mage from user group by sex,name
	 * <p>
	 * [
	 * {$group:{_id:{sex:"$sex",name:"$name"},mage:{$max:"$age"}}},
	 * {$project:{sex:"$_id.sex",name:"_id.name","mage":"$mage"}}
	 * ]
	 *
	 * @return
	 */
	private List<Document> buildGroupDoc() {
		List<Document> docs = new ArrayList<>(2);
		Document _group = new Document();
		// 用来存放group的字段
		Document _id = new Document();
		for (int i = 0; i < this.groups.length; i++) {
			Ele group = this.groups[i];
			if (group.getAlias()== null || "".equals(group.getAlias())) {
				group.as("group_" + i);
			}
			_id.putAll(group.build());
		}
		_group.put("_id", _id);

		// 如果没有定义select， 则默认以group的为准
		if (selects == null) {
			selects = new Ele[groups.length];
			for (int i = 0; i < this.groups.length; i++) {
				Ele group = this.groups[i];
				selects[i] = Field.of(group.getAlias());
			}
		}

		for (Ele select : this.selects) {
			if (!(select instanceof Field) && !(select instanceof Value)) {
				_group.putAll(select.build());
			}
		}

		docs.add(new Document("$group", _group));
		Document _project = new Document();
		// 提取在group by 中表达式的值
		_project.put("_id", 0);

		for (Ele select : this.selects) {
			if (select instanceof Field) {
				Field f = (Field) select;
				if (_id.keySet().contains(f.getKey())) {
					_project.put(select.getAlias(), "$_id." + f.getKey());
				} else {
					throw new MongoException("分组查询中select条件必须为聚合函数或group的属性值");
				}
			} else if (select instanceof Value) {
				_project.putAll(select.build());
			} else if (select instanceof Aggr) {
				_project.put(select.getAlias(), "$" + select.getAlias());
			} else {
				throw new MongoException("分组查询中select条件必须为聚合函数或group的属性值");
			}
		}
		docs.add(new Document("$project", _project));

		return docs;
	}

	public String getCollectionName() {
		if(innerQuery !=null){
			return innerQuery.getCollectionName();
		}
		return collectionName;
	}
}
